
#include "cpu/cpu.h"
#include "os_cfg.h"
#include "comm/cpu_instr.h"

//这里创建了一个GDT 的数组
static segment_desc_t gdt_table[GDT_TABLE_SIZE];

/**
 * 设置段描述符
 */
// selector 是选择子， base : 低地址， limit:界限, attr : 属性
// 这是一个 对每个 gdt 进行初始化的函数
void segment_desc_set(int selector, uint32_t base, uint32_t limit, uint16_t attr) {
    segment_desc_t * desc = gdt_table + selector  / sizeof(segment_desc_t);  // 这里 可以不除以  sizeof(segment_desc_t) 的，dgt表是 4个字节，4个字节的往前走的。


	// 如果界限比较长，将长度单位换成4KB
	if (limit > 0xfffff) {
		attr |= 0x8000; 
		limit /= 0x1000;
	}
    desc->limit15_0 = limit & 0xffff;
	desc->base15_0 = base & 0xffff;
	desc->base23_16 = (base >> 16) & 0xff;
	desc->attr = attr | (((limit >> 16) & 0xf) << 8);  // 这里 在attr里面设置了 limit 的值。
	desc->base31_24 = (base >> 24) & 0xff;
}

/**             
 * 初始化GDT
 */
//对每个表 进行循环的初始化。
void init_gdt(void) {
	// 全部清空
    for (int i = 0; i < GDT_TABLE_SIZE; i++) {
        segment_desc_set(i * sizeof(segment_desc_t), 0, 0, 0);  // 这里是在清空每个表。
    }


    //数据段    
    //注意这里的 SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_DATA| SEG_TYPE_RW | SEG_D  这个参数是对应的 segment_desc_set 函数中的参数的
    segment_desc_set(KERNEL_SELECTOR_DS, 0x00000000, 0xFFFFFFFF,
                     SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_DATA
                     | SEG_TYPE_RW | SEG_D);

    // 只能用非一致代码段，以便通过调用门更改当前任务的CPL执行关键的资源访问操作
    segment_desc_set(KERNEL_SELECTOR_CS, 0x00000000, 0xFFFFFFFF,
                     SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_CODE
                     | SEG_TYPE_RW | SEG_D);


    // 加载gdt, 使用的是之前的函数。
    lgdt((uint32_t)gdt_table, sizeof(gdt_table));
}

/**
 * CPU初始化
 */
void cpu_init (void) {
    init_gdt();
}
